package gitbox;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

public class client implements Runnable, Serializable{
    
    // Declaration section
    // The default port
    // clientClient: the client socket
    // os: the output stream
    // is: the input stream
    static String username;

    static String host="127.0.0.1";
    static int port_number=2222; // Port komunikasi dengan server
    static String tambahan_no_revisi = null;
    
    static Socket clientSocket = null;
    static ServerSocket serverSocket = null;
    static BufferedReader inputLine = null;
    
    static boolean closed = false;

    static int count = 0;

    static ConcurrentHashMap<String, Metadata> BeforeStatus = null;
    static ConcurrentHashMap<String, Metadata> NowStatus = null;
    static ConcurrentHashMap<String, Metadata> ServerStatus = null;

    static Thread threadlistening;
    static File directory;
    static File[] ListFile;

    static InputStream is =null;
    static OutputStream os = null;
    static ObjectInputStream ois = null;
    static ObjectOutputStream oos = null;

    static int statusthreadrunning = 0; // 0 tidak running, 1 running
    static ArrayList<Message> ListMessage = null;

    static Message MessageIn = null;
    static Message MessageOut = null;
    static String Workdir = null;
    
    // Constructor
    public client(String workdirpath) throws IOException{
        ConnectToServer();
        if(clientSocket.isBound()){
            System.out.println("MAIN--Start--");
            System.out.println("MAIN_binding");
            System.out.println(clientSocket);
        }
        Workdir = workdirpath;
        directory = new File(workdirpath);
        // Inisialisasi Attribut
        BeforeStatus = new ConcurrentHashMap<String, Metadata>();
        NowStatus = new ConcurrentHashMap<String, Metadata>();
        ServerStatus = new ConcurrentHashMap<String, Metadata>();
        ListMessage = new ArrayList<Message>();
        
        // Inisialisasi Method
        //ReadListFile();
        FillMessage();
        threadlistening = new Thread(this);
        threadlistening.start();
        statusthreadrunning = 1;
    }

    public Socket getSocket(){
        return clientSocket;
    }

    public void Login(String username, String password){
        System.out.println("MAIN_Login");
        try {
            HashMap<String,String> tmphash = new HashMap<String,String>();
            tmphash.put(username,password);
            os = clientSocket.getOutputStream();
            oos = new ObjectOutputStream(os);
            oos.writeObject(tmphash);
            System.out.println("MAIN_data-login " + oos);
        } catch (IOException ex) {
            Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public void ChangeWorkDir(String workdirpath){
        Workdir = workdirpath;
       directory = new File(workdirpath);
    }

    public void ReadListFile(){
        ListFile = directory.listFiles();
        //BeforeStatus = NowStatus; //harusnya g perlu ada ini juga gpp
        NowStatus.clear();
        for (int index = 0; index < ListFile.length; index++)
        {
           //Print out the name of files in the directory
           //System.out.println(ListFile[index].toString());
           //Metadata mt = new Metadata(ListFile[index].getName(),ListFile[index].getTotalSpace(),ListFile[index].lastModified());
           System.out.println("list file-nya : "+ListFile[index].getName()+" "+ListFile[index].lastModified());
           //NowStatus.put(ListFile[index].getName().toString(), mt);
           NowStatus.put(ListFile[index].getName().toString(), new Metadata(ListFile[index].getName(),ListFile[index].getTotalSpace(),ListFile[index].lastModified()));
        }

        //ini yang bagian nyocokin sama before
        MapDifference<String, Metadata> mapDiff = Maps.difference(BeforeStatus, NowStatus);
        Metadata nilai, nilai2;

        for (String key : mapDiff.entriesInCommon().keySet())
        {
            nilai = mapDiff.entriesInCommon().get(key);
            nilai2 = ServerStatus.get(key);
            if ((nilai2 == null) || (nilai.getLastModified() > nilai2.getLastModified()))
            {
                //kirim file ke server ( belum )
                SendFile(key);
            }
            else if (nilai.getLastModified() < nilai2.getLastModified())
            {
                if (nilai2.getSize()>=0)
                {
                    //minta dari server ( belum )
                    MessageOut = new Message("CLIENT-RequestFile",key);
                    System.out.println("CLIENT-MemintaFile");
                    sendMessageToServer();
                }
                else
                {
                    File fldelete = new File(Workdir + "\\" + key);
                    if(fldelete.delete()){
                        System.out.println("Client--Mendelete File");
                    }
                    BeforeStatus.remove(key);
                }
            }
            ServerStatus.remove(key);
        }
        for (String key : mapDiff.entriesOnlyOnLeft().keySet())
        {
            nilai = mapDiff.entriesOnlyOnLeft().get(key);
            nilai2 = ServerStatus.get(key);
            if (nilai2!=null)
                System.out.println("kiri only ::: nilai1 = "+nilai.getLastModified()+", nilai2 = "+nilai2.getLastModified());
            if ((nilai2 == null) || (nilai.getLastModified() >= nilai2.getLastModified()))
            {
                //kirim pesan delete file ini ( belum )
                BeforeStatus.remove(key);
                MessageOut = new Message("CLIENT-DeleteFile",tambahan_no_revisi+"?"+key);
                System.out.println("CLIENT-MemintaDeleteFile");
                sendMessageToServer();
            }
            else if (nilai.getLastModified() < nilai2.getLastModified())
            {
                if (nilai2.getSize()>=0)
                {
                    //minta dari server ( belum )
                    MessageOut = new Message("CLIENT-RequestFile",key);
                    //SendFile(Workdir + tambahan_no_revisi + "\\" + key);
                    System.out.println("CLIENT-MemintaFile");
                    sendMessageToServer();
                    BeforeStatus.put(key, nilai2);
                }
                else
                {
                    File fldelete = new File(Workdir + "\\" + key);
                    if(fldelete.delete()){
                        System.out.println("Client--Mendelete File");
                    }
                    BeforeStatus.remove(key);
                }
            }
            ServerStatus.remove(key);
        }
        for (String key : mapDiff.entriesOnlyOnRight().keySet())
        {
            nilai = mapDiff.entriesOnlyOnRight().get(key);
            nilai2 = ServerStatus.get(key);
            if (nilai2!=null)
                System.out.println("kanan only ::: nilai1 = "+nilai.getLastModified()+", nilai2 = "+nilai2.getLastModified());
            if ((nilai2 == null) || (nilai.getLastModified() > nilai2.getLastModified()))
            {
                BeforeStatus.put(key, nilai);
                //trus kirim ke server ( belum )
                //System.out.println(Workdir + "\\" + key);
                SendFile(key);
            }
            else if (nilai.getLastModified() < nilai2.getLastModified())
            {
                if (nilai2.getSize()>=0)
                {
                    //minta dari server ( belum )
                    BeforeStatus.put(key, nilai2);
                    MessageOut = new Message("CLIENT-RequestFile",key);
                    System.out.println("CLIENT-MemintaFile");
                    sendMessageToServer();
                }
                else
                {
                    File fldelete = new File(Workdir + "\\" + key);
                    if(fldelete.delete()){
                        System.out.println("Client--Mendelete File");
                    }
                    BeforeStatus.remove(key);
                }
            }
            ServerStatus.remove(key);
        }
        for (String key : mapDiff.entriesDiffering().keySet())
        {
            MapDifference.ValueDifference<Metadata> beda = mapDiff.entriesDiffering().get(key);
            System.out.println("hohohohohohoho "+key+"-----------------------");
            if (beda.leftValue().getLastModified()<beda.rightValue().getLastModified())
                BeforeStatus.put(key, beda.rightValue());
            nilai = BeforeStatus.get(key);
            nilai2 = ServerStatus.get(key);
            if (nilai2!=null)
                System.out.println("kiri kanan not only ::: nilai1 = "+nilai.getLastModified()+", nilai2 = "+nilai2.getLastModified());
//            System.out.println("bedanya apa" + nilai.getLastModified() + ", " + nilai2.getLastModified());
            if ((nilai2==null)||(nilai.getLastModified() > nilai2.getLastModified()))
            {
                //kirim file ini ke server ( belum )
                SendFile(key);
            }
            else if (nilai.getLastModified() < nilai2.getLastModified())
            {
                if (nilai2.getSize()>=0)
                {
                    //minta dari server ( belum )
                    MessageOut = new Message("CLIENT-RequestFile",key);
                    System.out.println("CLIENT-MemintaFile");
                    sendMessageToServer();
                    BeforeStatus.put(key, nilai2);
                }
                else
                {
                    File fldelete = new File(Workdir + "\\" + key);
                    if(fldelete.delete()){
                        System.out.println("Client--Mendelete File");
                    }
                    BeforeStatus.remove(key);
                }
            }
            ServerStatus.remove(key);
        }
        for (String key : ServerStatus.keySet())
        {
            nilai = ServerStatus.get(key);
            //minta file ini dari server ( belum )
            if (nilai.getSize()>=0)
            {
                MessageOut = new Message("CLIENT-RequestFile",key);
                System.out.println("CLIENT-MemintaFile");
                sendMessageToServer();
                BeforeStatus.put(key, nilai);
            }
        }
        
        System.out.println("THREAD-"+NowStatus);
    }
	
    public void FillMessage(){
        ListMessage.add(new Message("CLIENT-Exit","exit")); // 0
        ListMessage.add(new Message("CLIENT-RequestMetadata",null)); // 1
        ListMessage.add(new Message("CLIENT-RequestFile",null)); // 2
        ListMessage.add(new Message("CLIENT-DeleteFile",null)); // 3
        ListMessage.add(new Message("CLIENT-AcceptFile",null)); // 4
        ListMessage.add(new Message("CLIENT-Beres","")); // 5
    }
    public void ConnectToServer(){
        try {
            clientSocket = new Socket(host, port_number); //2222 Port download, upload
            
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host "+host);
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for the connection to the host "+host);
        }
        
    }
    public void SendFile(String sourcepathfile){
        // sendfile
          File myFile = new File (Workdir + "\\" + sourcepathfile);
          byte [] mybytearray  = new byte [(int)myFile.length()];
          FileInputStream fis;
          BufferedInputStream bis;
          OutputStream oss;
        try {
            fis = new FileInputStream(myFile);
            bis = new BufferedInputStream(fis);
            bis.read(mybytearray,0,mybytearray.length);
            //"\\" + tambahan_no_revisi +
            Message msgfile = new Message(""+tambahan_no_revisi +"\\"+myFile.getName().toString(),mybytearray);
            MessageOut = new Message("CLIENT-AcceptFile",msgfile);
            sendMessageToServer();
            System.out.println("Sending...");
            bis.close();
            fis.close();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
        } catch (java.io.IOException e){
            e.printStackTrace();
        }
    }
    public void ReceiveFile(String destpathfile){
        byte [] mybytearray  = null;
        //InputStream is = null;
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;
        try {
            //is = clientSocket.getInputStream();
            fos = new FileOutputStream(destpathfile);
            bos = new BufferedOutputStream(fos);
            Message msgfile = (Message) MessageIn.getContent();
            mybytearray = (byte[]) msgfile.getContent();
            bos.write(mybytearray);
            bos.flush();
            bos.close();
            fos.close();
        } catch (IOException ex) {
            Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public void DisconnectToServer(){
        try {
            statusthreadrunning = 0;
            os = clientSocket.getOutputStream();
            MessageOut = new Message("CLIENT-Exit","exit");
            sendMessageToServer();
            oos.close();
            ois.close();
            clientSocket.close();
        } catch (IOException ex) {
            Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void sendMessageToServer(){
        try {
            os = clientSocket.getOutputStream();
            oos = new ObjectOutputStream(os);
            oos.writeObject(MessageOut);
        } catch (IOException ex) {
            Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public static void main(String[] args) {
	count = 0;
	client Client = null;
        try {
            Client = new client("D:\\box2");
            username = "maulnick";
            Client.Login("maulnick","gdesrianti");
            //Client.ReadListFile();
            while(true){
                //LISTENING
                System.out.println("MAIN-waiting...");
                Client.is = clientSocket.getInputStream();
                Client.ois = new ObjectInputStream(is);
                MessageIn = (Message) ois.readObject();
                if(MessageIn.getTitle().equals("SERVER-Valid")){
                    // Valid
                    System.out.println("COMMAND-Valid");
                    System.out.println("Coba ngirim file");
                    // Testing ngirim file
                }else if(MessageIn.getTitle().equals("SERVER-Invalid")){
                    // Invalid && Exit
                    System.out.println("COMMAND-TidakValid");
                    break;
                }else if(MessageIn.getTitle().equals("SERVER-ServerStatus")){
                    Message temp = (Message)MessageIn.getContent();
                    ServerStatus = (ConcurrentHashMap<String, Metadata>) temp.getContent();
                    tambahan_no_revisi = temp.getTitle();
                    System.out.println("Server Status gan " + ServerStatus);
                    System.out.println("Client Status gan " + NowStatus);
                }else if(MessageIn.getTitle().equals("SERVER-AcceptFile")){
                    Message msgtemp = (Message) MessageIn.getContent();
                    Client.ReceiveFile(Workdir + "\\" + msgtemp.getTitle());
                }else if(MessageIn.getTitle().equals("SERVER-Exit")){
                    // Exit
                    System.out.println("COMMAND-Exit");
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
        }
        Client.DisconnectToServer();
        // Sending
        System.out.println("MAIN--end--");
    }           

    
    public void run() {
	// Updating clientstatus
        System.out.println("THREAD-Running = " + statusthreadrunning);
        while (true) {
            try {
                // ThreadRunning diaktifkan
                System.out.println("THREAD-***Update client mulai***");
                
                threadlistening.sleep(5000);
                if(statusthreadrunning==1){
                    // 1. Kirim pesan untuk request metadata dari server
                    MessageOut = new Message("CLIENT-RequestMetadata",null);
                    System.out.println("message out untuk request metadata");
                    sendMessageToServer();
                    
                    // 2. Update list file di directory
                    while (tambahan_no_revisi == null);
                    ReadListFile();
                    
                    MessageOut = new Message("CLIENT-Beres",tambahan_no_revisi);
                    sendMessageToServer();
                    System.out.println("THREAD--updatefile");
                }else{
                    break;
                }
                System.out.println("THREAD-***Update client selesai***");
            } catch (InterruptedException ex) {
                Logger.getLogger(client.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        System.out.println("THREAD-Closed");
    }
}